package com.mmall.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.mmall.beans.MenuTreeResponse;
import com.mmall.common.dtree.DTree;
import com.mmall.dao.SysMenuMapper;
import com.mmall.exception.ParamException;
import com.mmall.model.SysMenu;
import com.mmall.param.MenuParam;
import com.mmall.util.BeanValidator;
import com.mmall.util.DateUtil;
import com.mmall.util.LevelUtil;
import com.mmall.util.UUIDUtil;

/**
 * Created by gameloft9 on 2017/12/6.
 */
@Service
public class SysMenuService {

	@Resource
    private SysMenuMapper sysMenuMapper;
	@Resource
    private SysLogService sysLogService;

    /**
     * 添加菜单
     * @param menuName 菜单名称
     * @param menuUrl 访问链接
     * @param menuType 菜单类型
     * @param parentMenuId 父菜单id
     * @return String 菜单id
     * */
    public void save(MenuParam param){
    	BeanValidator.check(param);
        if(checkExist(param.getParentId(), param.getName(), param.getId())) {
            throw new ParamException("同一层级下存在相同名称的菜单");
        }
        SysMenu menu = SysMenu.builder().id(UUIDUtil.getUUID()).name(param.getName()).href(param.getHref())
        		.requestUrl(param.getRequestUrl()).icon(param.getIcon()).code(param.getCode())
        		.target(param.getTarget()).parentId(param.getParentId()).seq(param.getSeq())
        		.status(param.getStatus()).remark(param.getRemark()).build();

        menu.setLevel(LevelUtil.calculateLevel(getLevel(param.getParentId()),param.getParentId()));
        menu.setOperator("admin");
        menu.setOperateIp("127.0.0.1");
        //dept.setOperator(RequestHolder.getCurrentUser().getUsername());
        //dept.setOperateIp(IpUtil.getRemoteIp(RequestHolder.getCurrentRequest()));
        menu.setOperateTime(DateUtil.str2Date(DateUtil.currentDate()));
        sysMenuMapper.insertSelective(menu);
        sysLogService.saveMenuLog(null, menu);
    }
    
    /**
     * 删除菜单
     * @param menuId 菜单id
     * */
    public Boolean delete(String menuId){
    	SysMenu menu = sysMenuMapper.selectByPrimaryKey(menuId);
        Preconditions.checkNotNull(menu, "待删除的菜单不存在，无法删除");
        
        if (sysMenuMapper.countByParentId(menu.getId()) > 0) {
            throw new ParamException("当前菜单下面有子菜单，无法删除");
        }
    	return sysMenuMapper.deleteByPrimaryKey(menuId) > 0;
    }

    /**
     * 根据主键获取菜单信息
     * @param id 菜单主键
     * */
    public SysMenu getById(String id){
    	SysMenu sysMenu = sysMenuMapper.selectByPrimaryKey(id);
    	if (sysMenuMapper == null) {
            return null;
        }
    	return sysMenu;
    }

    /**
     * 更新菜单
     * @param id 菜单id
     * @param menuName 菜单名称
     * @param menuUrl 链接
     * @param requestUrl 后台访问路径
     * @param sort 排序号
     * @param idList 角色列表
     * */
    public void update(MenuParam param){
    	BeanValidator.check(param);
        if(checkExist(param.getParentId(), param.getName(), param.getId())) {
            throw new ParamException("同一层级下存在相同名称的菜单");
        }
        SysMenu before = sysMenuMapper.selectByPrimaryKey(param.getId());
        Preconditions.checkNotNull(before, "待更新的菜单不存在");

        SysMenu after = SysMenu.builder().id(param.getId()).name(param.getName()).href(param.getHref())
        		.requestUrl(param.getRequestUrl()).icon(param.getIcon()).code(param.getCode())
        		.target(param.getTarget()).parentId(param.getParentId()).seq(param.getSeq())
        		.status(param.getStatus()).remark(param.getRemark()).build();
        
        after.setLevel(LevelUtil.calculateLevel(getLevel(param.getParentId()),param.getParentId()));
        after.setUpdateUser("admin");
        after.setUpdateTime(DateUtil.str2Date(DateUtil.currentDate()));

        updateWithChild(before, after);
        sysLogService.saveMenuLog(before, after);
    }
    
    @Transactional
    private void updateWithChild(SysMenu before, SysMenu after) {
    	/*
        String newLevelPrefix = after.getLevel();
        String oldLevelPrefix = before.getLevel();
        if (!after.getLevel().equals(before.getLevel())) {
            List<SysDept> deptList = sysDeptMapper.getChildDeptListByLevel(before.getLevel());
            if (CollectionUtils.isNotEmpty(deptList)) {
                for (SysDept dept : deptList) {
                    String level = dept.getLevel();
                    if (level.indexOf(oldLevelPrefix) == 0) {
                        level = newLevelPrefix + level.substring(oldLevelPrefix.length());
                        dept.setLevel(level);
                    }
                }
                sysDeptMapper.batchUpdateLevel(deptList);
            }
        }
        */
    	sysMenuMapper.updateByPrimaryKey(after);
    }
    
    private boolean checkExist(String parentId, String menuName, String menuId) {
        return sysMenuMapper.countByNameAndParentId(parentId, menuName, menuId) > 0;
    }
    
    private String getLevel(String menuId) {
        SysMenu menu = sysMenuMapper.selectByPrimaryKey(menuId);
        if (menu == null) {
            return null;
        }
        return menu.getLevel();
    }
    
    public List<DTree> menuTree() {
        List<SysMenu> sysMenu = sysMenuMapper.getAllMenu();
        return menuListToTree(sysMenu);
    }

    public List<DTree> menuListToTree(List<SysMenu> menuLevelList) {
        if (CollectionUtils.isEmpty(menuLevelList)) {
            return Lists.newArrayList();
        }
        Multimap<String, DTree> levelMenuMap = ArrayListMultimap.create();
        List<DTree> dtrees = new ArrayList<DTree>();
        DTree d = null;
        for (SysMenu dto : menuLevelList) {
        	d = convertDtree(dto);
        	levelMenuMap.put(dto.getLevel(), d);
            if (LevelUtil.ROOT.equals(dto.getLevel())) {
            	dtrees.add(d);
            }
        }
        // 按照seq从小到大排序
        Collections.sort(menuLevelList, new Comparator<SysMenu>() {
            public int compare(SysMenu o1, SysMenu o2) {
                return Integer.parseInt(o1.getSeq()) - Integer.parseInt(o2.getSeq());
            }
        });
        // 递归生成树
        transformDeptTree(dtrees, LevelUtil.ROOT, levelMenuMap);
        return dtrees;
    }

    public DTree convertDtree(SysMenu menu){
    	DTree d = new DTree();
    	d.setId(menu.getId());
    	d.setTitle(menu.getName());
    	d.setLevel(menu.getLevel());
    	d.setParentId(menu.getParentId());
    	d.setSeq(menu.getSeq());
    	return d;
    }
    
    // level:0, 0, all 0->0.1,0.2
    // level:0.1
    // level:0.2
    public void transformDeptTree(List<DTree> dtrees, String level, Multimap<String, DTree> levelMenuMap) {
        for (int i = 0; i < dtrees.size(); i++) {
            // 遍历该层的每个元素
        	DTree dTree = dtrees.get(i);
            // 处理当前层级的数据
            String nextLevel = LevelUtil.calculateLevel(level, dTree.getId());
            // 处理下一层
            List<DTree> tempDeptList = (List<DTree>) levelMenuMap.get(nextLevel);
            if (CollectionUtils.isNotEmpty(tempDeptList)) {
                // 排序
                Collections.sort(tempDeptList, menuSeqComparator);
                // 设置下一层部门
                dTree.setChildren(tempDeptList);
                // 进入到下一层处理
                transformDeptTree(tempDeptList, nextLevel, levelMenuMap);
            }
        }
    }
    
    public List<MenuTreeResponse> menuListing() {
        List<SysMenu> sysMenu = sysMenuMapper.getAllMenu();
        return menuListToListing(sysMenu);
    }
    
    public List<MenuTreeResponse> menuListToListing(List<SysMenu> menuLevelList) {
        if (CollectionUtils.isEmpty(menuLevelList)) {
            return Lists.newArrayList();
        }
        Multimap<String, MenuTreeResponse> levelMenuMap = ArrayListMultimap.create();
        List<MenuTreeResponse> menuListing = new ArrayList<MenuTreeResponse>();
        MenuTreeResponse d = null;
        for (SysMenu dto : menuLevelList) {
        	d = convertMenuListing(dto);
        	levelMenuMap.put(dto.getLevel(), d);
            if (LevelUtil.ROOT.equals(dto.getLevel())) {
            	menuListing.add(d);
            }
        }
        // 按照seq从小到大排序
        Collections.sort(menuLevelList, new Comparator<SysMenu>() {
            public int compare(SysMenu o1, SysMenu o2) {
                return Integer.parseInt(o1.getSeq()) - Integer.parseInt(o2.getSeq());
            }
        });
        menuListing.get(0).setSpread(true);//设置展开第一层目录
        // 递归生成树
        transformDeptListing(menuListing, LevelUtil.ROOT, levelMenuMap);
        return menuListing;
    }
    
    public void transformDeptListing(List<MenuTreeResponse> menuListing, String level, Multimap<String, MenuTreeResponse> levelMenuMap) {
        for (int i = 0; i < menuListing.size(); i++) {
            // 遍历该层的每个元素
        	MenuTreeResponse mt = menuListing.get(i);
            // 处理当前层级的数据
            String nextLevel = LevelUtil.calculateLevel(level, mt.getId());
            // 处理下一层
            List<MenuTreeResponse> tempDeptList = (List<MenuTreeResponse>) levelMenuMap.get(nextLevel);
            if (CollectionUtils.isNotEmpty(tempDeptList)) {
                // 排序
                Collections.sort(tempDeptList, menuListingSeqComparator);
                // 设置下一层部门
                mt.setChildren(tempDeptList);
                // 进入到下一层处理
                transformDeptListing(tempDeptList, nextLevel, levelMenuMap);
            }
        }
    }
    
    public MenuTreeResponse convertMenuListing(SysMenu menu){
    	MenuTreeResponse m = new MenuTreeResponse();
    	m.setId(menu.getId());
    	m.setName(menu.getName());
    	m.setHref(menu.getHref());
    	m.setLevel(menu.getLevel());
    	m.setParentId(menu.getParentId());
    	m.setSeq(menu.getSeq());
    	return m;
    }
    
    public Comparator<DTree> menuSeqComparator = new Comparator<DTree>() {
        public int compare(DTree o1, DTree o2) {
            return Integer.parseInt(o1.getSeq()) - Integer.parseInt(o2.getSeq());
        }
    };
    
    public Comparator<MenuTreeResponse> menuListingSeqComparator = new Comparator<MenuTreeResponse>() {
        public int compare(MenuTreeResponse o1, MenuTreeResponse o2) {
            return Integer.parseInt(o1.getSeq()) - Integer.parseInt(o2.getSeq());
        }
    };
}
